Rework the Unicode hex input code. Now we only steal a single key
authorMatthias Clasen <mclasen@redhat.com>
Fri, 2 Sep 2005 18:14:59 +0000 (18:14 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Fri, 2 Sep 2005 18:14:59 +0000 (18:14 +0000)
2005-09-02  Matthias Clasen  <mclasen@redhat.com>

* gtk/gtkimcontextsimple.c: Rework the Unicode hex input
code. Now we only steal a single key combination, Ctrl-Shift-U,
instead of sixteen.
A hex Unicode sequence must be started with Ctrl-Shift-U, followed
by a sequence of hex digits entered with Ctrl-Shift still held.
Releasing one of the modifiers or pressing space while the modifiers
are still held commits the character. It is possible to erase
digits using backspace.
As an extension to the above, we also allow to start the sequence
with Ctrl-Shift-U, then release the modifiers before typing any
digits, and enter the digits without modifiers.
(#82011, Owen Taylor)

ChangeLog
ChangeLog.pre-2-10
gtk/gtkimcontextsimple.c

index f4e19fa615a7efbe33b004aa5630285380006670..e1c812971490095378178e38d20c777addd580bc 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2005-09-02  Matthias Clasen  <mclasen@redhat.com>
+
+       * gtk/gtkimcontextsimple.c: Rework the Unicode hex input
+       code. Now we only steal a single key combination, Ctrl-Shift-U,
+       instead of sixteen. 
+       A hex Unicode sequence must be started with Ctrl-Shift-U, followed
+       by a sequence of hex digits entered with Ctrl-Shift still held.
+       Releasing one of the modifiers or pressing space while the modifiers
+       are still held commits the character. It is possible to erase
+       digits using backspace.
+       As an extension to the above, we also allow to start the sequence
+       with Ctrl-Shift-U, then release the modifiers before typing any
+       digits, and enter the digits without modifiers.
+       (#82011, Owen Taylor)
+       
 2005-09-02  Matthias Clasen  <mclasen@redhat.com>
 
        * gtk/gtkmain.c (gtk_get_event_widget): If the window is destroyed,
index f4e19fa615a7efbe33b004aa5630285380006670..e1c812971490095378178e38d20c777addd580bc 100644 (file)
@@ -1,3 +1,18 @@
+2005-09-02  Matthias Clasen  <mclasen@redhat.com>
+
+       * gtk/gtkimcontextsimple.c: Rework the Unicode hex input
+       code. Now we only steal a single key combination, Ctrl-Shift-U,
+       instead of sixteen. 
+       A hex Unicode sequence must be started with Ctrl-Shift-U, followed
+       by a sequence of hex digits entered with Ctrl-Shift still held.
+       Releasing one of the modifiers or pressing space while the modifiers
+       are still held commits the character. It is possible to erase
+       digits using backspace.
+       As an extension to the above, we also allow to start the sequence
+       with Ctrl-Shift-U, then release the modifiers before typing any
+       digits, and enter the digits without modifiers.
+       (#82011, Owen Taylor)
+       
 2005-09-02  Matthias Clasen  <mclasen@redhat.com>
 
        * gtk/gtkmain.c (gtk_get_event_widget): If the window is destroyed,
index 26f0d4dd18165ff65e739adf4376dc4ba6c36311..8c6719e2242a0a0bef6fa22ac3a06259cb29f2f6 100644 (file)
@@ -1032,14 +1032,10 @@ gtk_im_context_simple_commit_char (GtkIMContext *context,
   len = g_unichar_to_utf8 (ch, buf);
   buf[len] = '\0';
 
-  context_simple->in_hex_sequence = FALSE;
-  
-  if (context_simple->tentative_match)
-    {
-      context_simple->tentative_match = 0;
-      context_simple->tentative_match_len = 0;
-      g_signal_emit_by_name (context_simple, "preedit_changed");
-    }
+  context_simple->in_hex_sequence = FALSE;  
+  context_simple->tentative_match = 0;
+  context_simple->tentative_match_len = 0;
+  g_signal_emit_by_name (context_simple, "preedit_changed");
 
   g_signal_emit_by_name (context, "commit", &buf);
 }
@@ -1116,7 +1112,7 @@ check_table (GtkIMContextSimple    *context_simple,
                {
                  context_simple->tentative_match = value;
                  context_simple->tentative_match_len = n_compose;
-
+               
                  g_signal_emit_by_name (context_simple, "preedit_changed");
 
                  return TRUE;
@@ -1134,14 +1130,21 @@ check_table (GtkIMContextSimple    *context_simple,
 }
 
 /* In addition to the table-driven sequences, we allow Unicode hex
- * codes entered with Ctrl-Shift held down as specified in ISO
- * 14755. 14755 actually allows a different set of modifiers to be
- * used at our discretion, but for now using Ctrl-Shift as in their
- * examples. While holding Ctrl-Shift, pressing space commits the
- * character, and pressing a non-hex-digit is an error.
+ * codes to be entered. The method chosen here is similar to the
+ * one recommended in ISO 14755, but not exactly the same, since we
+ * don't want to steal 16 valuable key combinations. 
+ * 
+ * A hex Unicode sequence must be started with Ctrl-Shift-U, followed
+ * by a sequence of hex digits entered with Ctrl-Shift still held.
+ * Releasing one of the modifiers or pressing space while the modifiers
+ * are still held commits the character. It is possible to erase
+ * digits using backspace.
+ *
+ * As an extension to the above, we also allow to start the sequence
+ * with Ctrl-Shift-U, then release the modifiers before typing any
+ * digits, and enter the digits without modifiers.
  */
-
-#define ISO_14755_MOD_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK)
+#define HEX_MOD_MASK (GDK_CONTROL_MASK | GDK_SHIFT_MASK)
 
 static gboolean
 check_hex (GtkIMContextSimple *context_simple,
@@ -1153,7 +1156,10 @@ check_hex (GtkIMContextSimple *context_simple,
   gulong n;
   gchar *nptr = NULL;
   gchar buf[7];
-  
+
+  context_simple->tentative_match = 0;
+  context_simple->tentative_match_len = 0;
+
   str = g_string_new (NULL);
   
   i = 0;
@@ -1189,14 +1195,11 @@ check_hex (GtkIMContextSimple *context_simple,
   else
     g_string_free (str, TRUE);
 
-  /* don't allow invalid Unicode or nul bytes */
-  if (n == 0 || !g_unichar_validate (n))
-    return FALSE;
-  
-  context_simple->tentative_match = n;
-  context_simple->tentative_match_len = n_compose;
-  
-  g_signal_emit_by_name (context_simple, "preedit_changed");
+  if (g_unichar_validate (n))
+    {
+      context_simple->tentative_match = n;
+      context_simple->tentative_match_len = n_compose;
+    }
   
   return TRUE;
 }
@@ -1313,22 +1316,43 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
   GSList *tmp_list;  
   int n_compose = 0;
   gboolean have_hex_mods;
+  gboolean is_hex_start;
+  gboolean is_space;
+  gboolean is_backspace;
   guint hex_keyval;
   int i;
 
+  while (context_simple->compose_buffer[n_compose] != 0)
+    n_compose++;
+
   if (event->type == GDK_KEY_RELEASE)
     {
       if (context_simple->in_hex_sequence &&
          (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R ||
           event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R))
        {
-         if (context_simple->tentative_match)
+         if (context_simple->tentative_match &&
+             g_unichar_validate (context_simple->tentative_match))
            {
              gtk_im_context_simple_commit_char (context, context_simple->tentative_match);
              context_simple->compose_buffer[0] = 0;
+
+           }
+         else if (n_compose == 0)
+           {
+             context_simple->modifiers_dropped = TRUE;
            }
          else
-           context_simple->in_hex_sequence = 0;
+           {
+             /* invalid hex sequence */
+             gdk_display_beep (gdk_drawable_get_display (event->window));
+             
+             context_simple->tentative_match = 0;
+             context_simple->in_hex_sequence = FALSE;
+             context_simple->compose_buffer[0] = 0;
+             
+             g_signal_emit_by_name (context_simple, "preedit_changed");
+           }
 
          return TRUE;
        }
@@ -1336,37 +1360,78 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
        return FALSE;
     }
 
-  /* Ignore modifier key presses
-   */
+  /* Ignore modifier key presses */
   for (i=0; i < G_N_ELEMENTS (gtk_compose_ignore); i++)
     if (event->keyval == gtk_compose_ignore[i])
       return FALSE;
 
-  have_hex_mods = (event->state & (ISO_14755_MOD_MASK)) == ISO_14755_MOD_MASK;
+  if (context_simple->in_hex_sequence && context_simple->modifiers_dropped)
+    have_hex_mods = TRUE;
+  else
+    have_hex_mods = (event->state & (HEX_MOD_MASK)) == HEX_MOD_MASK;
+  is_hex_start = event->keyval == GDK_U;
+  is_space = event->keyval == GDK_space || event->keyval == GDK_KP_Space;
+  is_backspace = event->keyval == GDK_BackSpace;
   hex_keyval = canonical_hex_keyval (event);
 
-  while (context_simple->compose_buffer[n_compose] != 0)
-    n_compose++;
-
   /* If we are already in a non-hex sequence, or
-   * this keystroke is not 14755 modifiers + hex digit, don't filter 
+   * this keystroke is not hex modifiers + hex digit, don't filter 
    * key events with accelerator modifiers held down.
    */
-  if ((n_compose > 0 && !context_simple->in_hex_sequence) || !have_hex_mods || !hex_keyval)
+  if (!have_hex_mods ||
+      (n_compose > 0 && !context_simple->in_hex_sequence) || 
+      (n_compose == 0 && !context_simple->in_hex_sequence && !is_hex_start) ||
+      (context_simple->in_hex_sequence && !hex_keyval && !is_space && !is_backspace))
     {
-      if (event->state &
-          (gtk_accelerator_get_default_mod_mask () & ~GDK_SHIFT_MASK))
-        return FALSE;
+      if (event->state & (gtk_accelerator_get_default_mod_mask () & ~GDK_SHIFT_MASK))
+       return FALSE;
+    }
+  
+  /* Handle backspace */
+  if (context_simple->in_hex_sequence && have_hex_mods && is_backspace)
+    {
+      if (n_compose > 0)
+       {
+         n_compose--;
+         context_simple->compose_buffer[n_compose] = 0;
+          check_hex (context_simple, n_compose);
+       }
+      else
+       {
+         context_simple->in_hex_sequence = FALSE;
+       }
+
+      g_signal_emit_by_name (context_simple, "preedit_changed");
+      
+      return TRUE;
     }
+
+  /* Check for hex sequence start */
+  if (n_compose == 0 && !context_simple->in_hex_sequence && have_hex_mods && is_hex_start)
+    {
+      context_simple->compose_buffer[0] = 0;
+      context_simple->in_hex_sequence = TRUE;
+      context_simple->modifiers_dropped = FALSE;
+      context_simple->tentative_match = 0;
+
+      g_signal_emit_by_name (context_simple, "preedit_changed");
   
-  /* First key in sequence; decide if it's a 14755 hex sequence */
-  if (n_compose == 0)
-    context_simple->in_hex_sequence = have_hex_mods;
+      return TRUE;
+    }
   
-  /* Then, check for compose sequences
-   */
+  /* Then, check for compose sequences */
   if (context_simple->in_hex_sequence)
-    context_simple->compose_buffer[n_compose++] = hex_keyval ? hex_keyval : event->keyval;
+    {
+      if (hex_keyval)
+       context_simple->compose_buffer[n_compose++] = hex_keyval;
+      else if (!is_space)
+       {
+         /* non-hex character in hex sequence */
+         gdk_display_beep (gdk_drawable_get_display (event->window));
+         
+         return TRUE;
+       }
+    }
   else
     context_simple->compose_buffer[n_compose++] = event->keyval;
 
@@ -1378,16 +1443,30 @@ gtk_im_context_simple_filter_keypress (GtkIMContext *context,
       if (have_hex_mods)
         {
           /* space ends the sequence, and we eat the space */
-          if (n_compose > 1 &&
-              (event->keyval == GDK_space ||
-               event->keyval == GDK_KP_Space))
+          if (n_compose > 0 && is_space)
             {
-              gtk_im_context_simple_commit_char (context, context_simple->tentative_match);
-              context_simple->compose_buffer[0] = 0;
-              return TRUE;
+             if (context_simple->tentative_match &&
+                 g_unichar_validate (context_simple->tentative_match))
+               {
+                 gtk_im_context_simple_commit_char (context, context_simple->tentative_match);
+                 context_simple->compose_buffer[0] = 0;
+               }
+             else
+               {
+                 /* invalid hex sequence */
+                 gdk_display_beep (gdk_drawable_get_display (event->window));
+
+                 context_simple->tentative_match = 0;
+                 context_simple->in_hex_sequence = FALSE;
+                 context_simple->compose_buffer[0] = 0;
+               }
             }
-          else if (check_hex (context_simple, n_compose))
-            return TRUE;
+          else if (!check_hex (context_simple, n_compose))
+           gdk_display_beep (gdk_drawable_get_display (event->window));
+         
+         g_signal_emit_by_name (context_simple, "preedit_changed");
+
+         return TRUE;
         }
     }
   else
@@ -1412,13 +1491,15 @@ static void
 gtk_im_context_simple_reset (GtkIMContext *context)
 {
   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
-  
+
   context_simple->compose_buffer[0] = 0;
 
   if (context_simple->tentative_match)
     gtk_im_context_simple_commit_char (context, context_simple->tentative_match);
-
+  
   context_simple->in_hex_sequence = FALSE;
+
+  g_signal_emit_by_name (context_simple, "preedit_changed");
 }
 
 static void     
@@ -1432,32 +1513,32 @@ gtk_im_context_simple_get_preedit_string (GtkIMContext   *context,
   
   GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
 
-  if (context_simple->tentative_match)
+  if (context_simple->in_hex_sequence)
     {
-      if (context_simple->in_hex_sequence)
-        {
-          int hexchars = 0;
-          
-          while (context_simple->compose_buffer[hexchars] != 0)
-            {
-              len += g_unichar_to_utf8 (gdk_keyval_to_unicode (context_simple->compose_buffer[hexchars]),
-                                        outbuf + len);
-              ++hexchars;
-            }
-        }
-      else
-        {
-          len = g_unichar_to_utf8 (context_simple->tentative_match, outbuf);
-        }
-      
+      int hexchars = 0;
+         
+      outbuf[0] = 'u';
+      len = 1;
+
+      while (context_simple->compose_buffer[hexchars] != 0)
+       {
+         len += g_unichar_to_utf8 (gdk_keyval_to_unicode (context_simple->compose_buffer[hexchars]),
+                                   outbuf + len);
+         ++hexchars;
+       }
+
       g_assert (len < 25);
       outbuf[len] = '\0';      
     }
+  else if (context_simple->tentative_match)
+    {
+      len = g_unichar_to_utf8 (context_simple->tentative_match, outbuf);
+    }
   else
     {
       outbuf[0] = '\0';
     }
-  
+      
   if (str)
     *str = g_strdup (outbuf);
 
@@ -1475,7 +1556,7 @@ gtk_im_context_simple_get_preedit_string (GtkIMContext   *context,
     }
 
   if (cursor_pos)
-    *cursor_pos = (context_simple->tentative_match ? len : 0);
+    *cursor_pos = len;
 }
 
 /**